1 //------------------------------------------------------------------------------
2 // <copyright file="parse.cs" company="Microsoft">
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
14 //------------------------------------------------------------------------------
30 private bool mainseen
= false;
35 Console
.WriteLine("dataClass1 token=["+tok
+"]\n");
38 if (id
== Tok
.T_EXTERN
|| id
== Tok
.T_STATIC
|| id
== Tok
.T_AUTO
)
40 e
.setClassId(tok
.getId());
43 Console
.WriteLine("dataClass2 token=["+tok
+"]\n");
48 e
.setClassId(Tok
.T_DEFCLASS
); /* default to current context */
56 Console
.WriteLine("dataType1 token=["+tok
+"]\n");
58 e
.setSign(Tok
.T_SIGNED
);
59 if (id
== Tok
.T_SIGNED
|| id
== Tok
.T_UNSIGNED
)
65 Console
.WriteLine("dataType2 token=["+tok
+"]\n");
68 if (id
== Tok
.T_VOID
|| id
== Tok
.T_INT
)
73 Console
.WriteLine("dataType3 token=["+tok
+"]\n");
76 else if (id
== Tok
.T_LONG
)
77 Io
.ICE("Unhandled type LONG");
78 else if (id
== Tok
.T_CHAR
)
79 Io
.ICE("Unhandled type CHAR");
80 else if (id
== Tok
.T_FLOAT
)
81 Io
.ICE("Unhandled type FLOAT");
82 else if (id
== Tok
.T_DOUBLE
)
83 Io
.ICE("Unhandled type DOUBLE");
85 e
.setTypeId(Tok
.T_DEFTYPE
);
92 if (tok
.getFirstChar() != '(')
93 io
.Abort("Expected '('");
95 v
= new VarList(); /* init the param list */
96 tok
.scan(); /* get the next token */
97 while (tok
.getFirstChar() != ')')
100 e
.setClassId(Tok
.T_PARAM
); /* mark this as a parameter */
101 dataType(e
); /* get the specified datatype */
102 e
.setName(tok
.getValue()); /* copy the variable name */
103 v
.add(e
); /* add parameter to param list */
105 if (tok
.getFirstChar() == ',') /* if more params */
106 tok
.scan(); /* get the next token */
108 tok
.scan(); /* move to the next token */
116 Console
.WriteLine("outerDecl token=["+tok
+"]\n");
119 CommentHolder(); /* mark the position in insn stream */
124 if (io
.getNextChar() == '(')
133 emit
.BeginModule(); // need assembly module
146 * validate this declaration as a potential variable
148 int id
= e
.getClassId();
150 Console
.WriteLine("addInner e=["+e
+"]\n");
152 if (id
== Tok
.T_EXTERN
|| id
== Tok
.T_STATIC
)
153 io
.Abort("Cannot allocate static within a method\n");
154 if (paramvar
.FindByName(e
.getName()) != null) /* cannot redefine param name */
155 io
.Abort("Cannot redefine parameter name '" + e
.getName() + "'\n");
156 if (localvar
.FindByName(e
.getName()) != null) /* check not already set */
157 io
.Abort("Local variable '" + e
.getName() + "' already defined\n");
162 * parse an inner declaration
166 while (tok
.IsDeclKeyword())
170 Console
.WriteLine("declInner1 token=["+tok
+"]\n");
173 CommentHolder(); /* mark comment position in insn stream */
175 dataClass(e
); // parse the data class (static, auto, etc)
176 dataType(e
); // parse the type (int, unsigned, etc)
177 e
.setName(tok
.getValue()); // save the name in var
179 Console
.WriteLine("declInner2.0 token=["+tok
+"]\n");
180 Console
.WriteLine("declInner2.0 e=["+e
+"]\n");
182 addInner(e
); /* add this variable */
184 * loop while there are additional variable names
186 while (io
.getNextChar() == ',')
189 if (tok
.getFirstChar() != ',')
190 io
.Abort("Expected ','");
192 if (tok
.getId() != Tok
.T_IDENT
)
193 io
.Abort("Expected identifier");
194 e
.setName(tok
.getValue()); /* use value as the variable name */
196 Console
.WriteLine("declInner2.1 token=["+tok
+"]\n");
197 Console
.WriteLine("declInner2.1 e=["+e
+"]\n");
199 addInner(e
); /* reuse the class & type */
202 * move beyond end of statement indicator
205 if (tok
.getFirstChar() != ';')
206 io
.Abort("Expected ';'");
208 tok
.scan(); /* get token ready nor next parser step */
210 Console
.WriteLine("declInner3 token=["+tok
+"]\n");
218 Console
.WriteLine("addOuter e=["+e
+"]\n");
221 * validate this declaration as a potential variable
223 if (e
.getClassId() == Tok
.T_AUTO
)
224 io
.Abort("?Cannot allocate automatic variable outside a function\n");
225 if (staticvar
.FindByName(e
.getName()) != null)
226 io
.Abort("?Cannot redefine static name '" + e
.getName() + "'\n");
231 * declOuter presumes that class & type have already been parsed
232 * this is done to distinguish between a function declaration and a
233 * variable declaration
235 void declOuter(Var e
)
238 Console
.WriteLine("declOuter1 token=["+tok
+"]\n");
240 e
.setName(tok
.getValue()); /* use value as the variable name */
241 addOuter(e
); /* add this variable */
242 emit
.FieldDef(e
); /* issue the declaration */
243 if (e
.getClassId() == Tok
.T_DEFCLASS
)
244 e
.setClassId(Tok
.T_STATIC
); /* make sure it knows its storage class */
247 * loop while there are additional variable names
249 while (io
.getNextChar() == ',')
252 if (tok
.getFirstChar() != ',')
253 io
.Abort("Expected ','");
255 if (tok
.getId() != Tok
.T_IDENT
)
256 io
.Abort("Expected identifier");
257 e
.setName(tok
.getValue()); /* use value as the variable name */
258 addOuter(e
); /* add this variable */
259 emit
.FieldDef(e
); /* issue the declaration */
260 if (e
.getClassId() == Tok
.T_DEFCLASS
)
261 e
.setClassId(Tok
.T_STATIC
); /* make sure it knows its storage class */
264 * move beyond end of statement indicator
267 if (tok
.getFirstChar() != ';')
268 io
.Abort("Expected ';'");
272 Console
.WriteLine("declOuter2 token=["+tok
+"]\n");
279 Console
.WriteLine("declFunc token=["+tok
+"]\n");
281 CommentHolder(); // start new comment
282 e
.setName(tok
.getValue()); /* value is the function name */
283 if (e
.getName().Equals("main"))
286 io
.Abort("Using main entrypoint when generating a DLL");
289 staticvar
.add(e
); /* add function name to static VarList */
290 paramvar
= paramList(); // track current param list
291 e
.setParams(paramvar
); // and set it in func var
292 localvar
= new VarList(); // track new local parameters
296 if (tok
.getFirstChar() != '{')
297 io
.Abort("Expected '{'");
298 blockOuter(null, null);
307 * parse the outerBlock seperately from inner block.
308 * specifically parse the declarations at the beginning of an outerblock
309 * the variable r tracks if the last statement was a return or not
311 void blockOuter(String olabel
, String ilabel
)
315 Console
.WriteLine("blockOuter1 token=["+tok
+"]\n");
317 CommentToken(); /* mark the position in insn stream */
320 Console
.WriteLine("blockOuter2 token=["+tok
+"]\n");
323 emit
.LocalVars(localvar
);
325 while (tok
.getFirstChar() != '}')
328 Console
.WriteLine("blockOuter3 token=["+tok
+"]\n");
331 io
.Abort("Expected statement, end of file encountered");
335 case Tok
.T_IF
: fcIf(olabel
, ilabel
); continue; /* fcIf updates tok */
336 case Tok
.T_WHILE
: fcWhile(); break;
337 case Tok
.T_FOR
: fcFor(); break;
338 case Tok
.T_BREAK
: fcBreak(olabel
); break;
339 case Tok
.T_CONTINUE
: fcContinue(ilabel
); break;
340 case Tok
.T_RETURN
: fcReturn(); r
= true; break;
341 default: statement(); break;
344 Console
.WriteLine("blockOuter4 token=["+tok
+"]\n");
347 if (!r
) /* if no outerscope return specified, try to default */
349 Var e
= emit
.GetFunc(); /* get function def */
350 if (e
.getTypeId() != Tok
.T_VOID
) /* make sure we can default to void */
352 if (!e
.getName().Equals("main")) /* is it not main? */
353 io
.Abort("No return value specified for non void function");
354 emit
.LoadConst("0"); /* special case a dummy ret value */
358 CommentToken(); /* mark the position in insn stream */
359 tok
.scan(); /* read beyond end of block */
361 Console
.WriteLine("blockOuter5 token=["+tok
+"]\n");
365 /* recognize and translate a statement block */
366 void blockInner(String olabel
, String ilabel
)
369 Console
.WriteLine("blockInner1 token=["+tok
+"]\n");
371 if (tok
.getFirstChar() == '{') /* if begin, then get next token */
373 CommentToken(); /* mark the position in insn stream */
376 Console
.WriteLine("blockInner2 token=["+tok
+"]\n");
378 if (tok
.IsDeclKeyword())
379 io
.Abort("Illegal inner declaration of variable");
380 while (tok
.getFirstChar() != '}')
384 case Tok
.T_IF
: fcIf(olabel
, ilabel
); continue; /* fcIf updates tvp */
385 case Tok
.T_WHILE
: fcWhile(); break;
386 case Tok
.T_FOR
: fcFor(); break;
387 case Tok
.T_BREAK
: fcBreak(olabel
); break;
388 case Tok
.T_CONTINUE
: fcContinue(ilabel
); break;
389 case Tok
.T_RETURN
: fcReturn(); break;
390 default: statement(); break;
393 Console
.WriteLine("blockInner3 token=["+tok
+"]\n");
396 CommentToken(); /* mark the begin of source comment */
397 tok
.scan(); /* read beyond end of blockInner */
403 case Tok
.T_IF
: fcIf(olabel
, ilabel
); break;
404 case Tok
.T_WHILE
: fcWhile(); break;
405 case Tok
.T_FOR
: fcFor(); break;
406 case Tok
.T_BREAK
: fcBreak(olabel
); break;
407 case Tok
.T_CONTINUE
: fcContinue(ilabel
); break;
408 case Tok
.T_RETURN
: fcReturn(); break;
409 default: statement(); break;
415 * the flow of control routines
418 /* gen a unique label */
421 StringBuilder sb
= new StringBuilder("L");
422 sb
.Append(label_count
++);
423 return (sb
.ToString());
427 * parse an if construct
429 * if (e) stmt-block else stmt-block
431 void fcIf(String olabel
, String ilabel
)
436 CommentHolder(); /* mark the position in insn stream */
438 if (tok
.getFirstChar() != '(')
439 io
.Abort("Expected '('");
445 label2
= String
.Copy(label1
);
447 emit
.Branch("brfalse", label1
);
449 blockInner(olabel
, ilabel
); /* parse block or single statement */
452 Console
.WriteLine("fcIf token=["+tok
+"]\n");
454 if (tok
.getId() == Tok
.T_ELSE
) /* sniff for else clause */
457 emit
.Branch("br", label2
);
460 tok
.scan(); /* get next token after else */
461 blockInner(olabel
, ilabel
); /* outer label, top of loop */
466 /* parse and translate a while statement */
469 String label1
= newLabel();
470 String label2
= newLabel();
472 CommentHolder(); /* mark the position in insn stream */
475 if (tok
.getFirstChar() != '(')
476 io
.Abort("Expected '('");
480 emit
.Branch("brfalse", label2
);
482 blockInner(label2
, label1
); /* outer label, top of loop */
483 emit
.Branch("br", label1
);
490 String label1
= newLabel();
491 String label2
= newLabel();
492 String label3
= newLabel();
493 String label4
= newLabel();
495 CommentHolder(); /* mark the position in insn stream */
497 if (tok
.getFirstChar() != '(')
498 io
.Abort("Expected '('");
501 * the assignment statement
503 tok
.scan(); /* i=0 */
504 if (tok
.getFirstChar() == ';') /* allow null assignment */
507 tok
.scan(); /* move beyond null statement */
511 statement(); /* parse the statement */
512 /* statement also closes the comment */
519 CommentHolder(); /* mark the position in insn stream */
521 if (tok
.getFirstChar() == ';') /* allow null test */
523 CommentFill("<no test>");
524 tok
.scan(); /* move past the semi */
528 boolExpr(); /* i<max */
529 if (tok
.getFirstChar() != ';')
530 io
.Abort("Expected ';'");
533 emit
.Branch("brfalse", label4
); /* test for result */
535 emit
.Branch("br", label3
); /* emit. branch to statement block */
538 * the increment statement
540 CommentHolder(); /* mark comment position in insn stream */
543 if (tok
.getFirstChar() == ')') /* allow null increment statement */
545 CommentFill(); /* end comment here */
546 tok
.scan(); /* move to next token */
551 * this is similar to assign, but ends with ')'
553 String varname
= tok
.getValue();
555 if (tok
.getFirstChar() != '=')
556 io
.Abort("Expected '='");
559 emit
.Store(lookup_name(varname
));
560 if (tok
.getFirstChar() != ')')
561 io
.Abort("Expected ')'");
562 CommentFill(); /* end comment here */
563 tok
.scan(); /* move to next token */
565 emit
.Branch("br", label1
);
568 * the statement block
571 blockInner(label4
, label2
); /* outer label, top of loop */
572 emit
.Branch("br", label2
); /* go to 3rd term (usually incr) */
577 /* recognize and translate a break */
578 void fcBreak(String olabel
)
580 CommentHolder(); /* mark the position in insn stream */
582 emit
.Branch("br", olabel
);
584 io
.Abort("No loop to break from");
586 if (tok
.getFirstChar() != ';')
587 io
.Abort("Expected ';'");
592 /* parse a continue */
593 void fcContinue(String ilabel
)
595 CommentHolder(); /* mark the position in insn stream */
597 emit
.Branch("br", ilabel
);
599 io
.Abort("No loop to break from");
601 if (tok
.getFirstChar() != ';')
602 io
.Abort("Expected ';'");
610 Var e
= emit
.GetFunc();
611 CommentHolder(); /* mark the position in insn stream */
612 tok
.scan(); /* get the return value */
613 if (tok
.getFirstChar() == ';') /* if end of statment */
615 if (e
.getTypeId() != Tok
.T_VOID
)
616 io
.Abort("Expected value for return type");
620 if (e
.getTypeId() == Tok
.T_VOID
)
621 io
.Abort("Unexpected value for void return type");
622 boolExpr(); /* parse as expression */
624 emit
.Ret(); /* issue the return (value is on stack) */
626 tok
.scan(); /* move past the semi */
636 Console
.WriteLine("ident token=["+tok
+"]\n");
638 if (io
.getNextChar() == '(') /* this must be a function call */
640 String vname
= tok
.getValue();
642 tok
.scan(); /* eat the left paren */
643 tok
.scan(); /* get the first parameter (or none) */
644 if (tok
.getFirstChar() != ')') /* if there are params */
646 boolExpr(); /* parse first param */
647 while (tok
.getFirstChar() == ',') /* while there are more params */
649 tok
.scan(); /* get the next token */
650 boolExpr(); /* parse now as an expression */
652 if (tok
.getFirstChar() != ')') /* must find closing paren */
653 io
.Abort("Expected ')'");
654 tok
.scan(); /* move to next token */
656 e
= staticvar
.FindByName(vname
); /* find the symbol */
657 if (e
.getTypeId() == Tok
.T_VOID
) /* if function value is void */
658 io
.Abort("Using void function where expecting a value");
659 emit
.Call(e
); /* construct the call */
663 emit
.Load(lookup_name(tok
.getValue())); /* load this symbol */
664 tok
.scan(); /* get the next token */
671 Console
.WriteLine("factor token=["+tok
+"]\n");
673 if (tok
.getFirstChar() == '(')
675 tok
.scan(); /* get next token */
677 if (tok
.getFirstChar() != ')')
678 io
.Abort("Expected ')'");
679 tok
.scan(); /* get next token */
681 else if (tok
.getId() == Tok
.T_IDENT
)
687 emit
.LoadConst(tok
.getValue());
688 tok
.scan(); /* get next token */
694 if (tok
.getId() == Tok
.T_ADD_OP
) /* unary plus */
696 tok
.scan(); /* get the next token */
697 factor(); /* process the factor */
700 if (tok
.getId() == Tok
.T_SUB_OP
) /* unary minus */
703 if (tok
.getId() == Tok
.T_DIGITS
)
705 StringBuilder sb
= new StringBuilder("-"); /* recreate the String */
706 sb
.Append(tok
.getValue());
707 emit
.LoadConst(sb
.ToString()); /* emit. load const for String */
711 factor(); /* process the factor */
715 factor(); /* no unary, so pass this token down */
732 /* completion of term processing (called by term and firstTerm) */
735 while (tok
.getId() == Tok
.T_MUL_OP
|| tok
.getId() == Tok
.T_DIV_OP
)
739 case Tok
.T_MUL_OP
: termMult(); break;
740 case Tok
.T_DIV_OP
: termDiv(); break;
745 /* parse and translate math term */
752 /* check term with possible leading sign */
759 /* translate an add */
767 /* translate a subtract */
775 /* parse an expression */
779 Console
.WriteLine("mathExpr1 token=["+tok
+"]\n");
783 Console
.WriteLine("mathExpr2 token=["+tok
+"]\n");
785 while (tok
.getId() == Tok
.T_ADD_OP
|| tok
.getId() == Tok
.T_SUB_OP
)
789 case Tok
.T_ADD_OP
: exprAdd(); break;
790 case Tok
.T_SUB_OP
: exprSub(); break;
795 /* recognize and translate a relational "equal" */
803 /* recognize and translate a relational "not equal" */
813 /* recognize and translate a relational "less than" */
821 /* recognize and translate a relational "greater than" */
847 /* parse a relation */
852 Console
.WriteLine("relExpr token=["+tok
+"]\n");
856 case Tok
.T_EQ_OP
: relEQ(); break;
857 case Tok
.T_NEQ_OP
: relNEQ(); break;
858 case Tok
.T_LT_OP
: relLTR(); break;
859 case Tok
.T_GT_OP
: relGTR(); break;
860 case Tok
.T_GE_OP
: relGEQ(); break;
861 case Tok
.T_LE_OP
: relLEQ(); break;
865 /* parse factor with NOT */
869 Console
.WriteLine("notFactor token=["+tok
+"]\n");
871 if (tok
.getId() == Tok
.T_NOT_OP
)
880 /* recognize and translate a boolean OR */
888 /* recognize and transate an exclusize or (XOR) */
896 /* recognize and transate a bitwise AND */
904 /* parse an translate a boolean term */
908 while (tok
.getId() == Tok
.T_OR_OP
909 || tok
.getId() == Tok
.T_XOR_OP
910 || tok
.getId() == Tok
.T_AND_OP
)
914 case Tok
.T_OR_OP
: boolOr(); break;
915 case Tok
.T_XOR_OP
: boolXor(); break;
916 case Tok
.T_AND_OP
: boolAnd(); break;
919 Console
.WriteLine("termBool token=["+tok
+"]\n");
924 /* parse and translate a boolean expression */
928 int id
= tok
.getId();
929 if (id
== Tok
.T_LOG_OR_OP
|| id
== Tok
.T_LOG_AND_OP
)
931 String label1
= newLabel();
932 String label2
= newLabel();
933 String label3
= newLabel();
937 if (id
== Tok
.T_LOG_AND_OP
)
938 emit
.Branch("brfalse", label1
); // optimize the logical and
939 else if (id
== Tok
.T_LOG_OR_OP
)
940 emit
.Branch("brtrue", label2
); // optimize the logical or
946 emit
.Branch("brtrue", label2
);
947 emit
.Label(label1
); // false path
949 emit
.Branch("br",label3
);
950 emit
.Label(label2
); // true path
952 emit
.Label(label3
); // common path
956 Var
lookup_name(String s
)
959 if ((e
= localvar
.FindByName(s
)) == null)
960 if ((e
= paramvar
.FindByName(s
)) == null)
961 if ((e
= staticvar
.FindByName(s
)) == null)
963 io
.Abort("Undefined variable '"+ s
+ "'\n");
969 * parse a generic statement
971 * possible forms are:
974 * and <expr> can contain: function or arithmetic expressions
976 * so for a recursive descent parser we need to test for a possible
982 String vname
= tok
.getValue();
984 CommentHolder(); /* mark the position in insn stream */
985 switch (io
.getNextChar())
987 case '(': /* this is a function call */
989 if (tok
.getFirstChar() != '(')
990 io
.Abort("Expected '('");
991 tok
.scan(); /* get the first parameter (or none) */
992 if (tok
.getFirstChar() != ')') /* if there are params */
994 boolExpr(); /* parse first param */
995 while (tok
.getFirstChar() == ',') /* while there are more params */
997 tok
.scan(); /* get the next token */
998 boolExpr(); /* parse now as an expression */
1000 if (tok
.getFirstChar() != ')') /* must find closing paren */
1001 io
.Abort("Expected ')'");
1004 tok
.scan(); /* move to next token */
1005 e
= staticvar
.FindByName(vname
); /* find the symbol (e cannot be null) */
1007 if (e
.getTypeId() != Tok
.T_VOID
) /* if function value not void */
1008 emit
.Insn("pop"); /* then pop the unused return value */
1010 case '=': /* if we have equal, then parse assign */
1012 if (tok
.getFirstChar() != '=')
1013 io
.Abort("Expected '='");
1017 emit
.Store(lookup_name(vname
));
1021 StringBuilder sb
= new StringBuilder();
1022 sb
.Append("Unexpected character '");
1023 sb
.Append(io
.getNextChar());
1025 io
.Abort(sb
.ToString());
1029 if (tok
.getFirstChar() != ';')
1030 io
.Abort("Expected ';'");
1034 public Parse(Io i
, Tok t
)
1038 staticvar
= new VarList(); // init the static variables list
1041 /* parse and translate a program */
1042 public void program()
1045 while (tok
.NotEOF())
1049 if (Io
.genexe
&& !mainseen
)
1050 io
.Abort("Generating executable with no main entrypoint");
1056 io
.commentBegin(tok
.getValue()); /* mark the begin of source comment */
1057 emit
.CommentHolder();
1058 emit
.CommentFill(io
.commentEndTok(tok
.getValue())); /* copy and emit comment */
1061 void CommentHolder()
1063 io
.commentBegin(tok
.getValue()); /* mark the begin of source comment */
1064 emit
.CommentHolder();
1069 emit
.CommentFill(io
.commentEndTok(tok
.getValue())); /* copy and emit comment */
1072 void CommentFill(String s
)
1074 emit
.CommentFill(io
.commentEndTok(tok
.getValue()) + s
); /* end the comment (no test) */
1077 void CommentFillPreTok()
1079 emit
.CommentFill(io
.commentEndPreTok(tok
.getValue())); /* copy and emit source */